trigger ONEN_Opp_MembershipStatusRollup on Opportunity (after insert, after update, before delete, after delete, after undelete) {
	
/*	

* get list of Opp Id's
* SELECT primary ConRoles for those id's, and distill into list of con id's
* SELECT 
* SELECT all Membership-type Opps for those contacts (ordered by date)
    - use relationship query here?
    - Q: what to do if have 2 memberships on same date from same contact?  Should we try to prevent this via trigger?
* dump into 2-dimentional map->list, i.e. ConID -> [Opps]
    - need to use array, not list or set for the inner part so will retain date order
* loop through map keys (contact ID's)
    - use last opp in array for end date, status, level, etc.
    - use first opp in array for join date
    - create Contact object for update, populate fields from the specified opp, add to array
* update contacts from array


for deletion, we're going to handle things differently for account-based vs individual-based opps
for individuals, we need to do before delete, since we need the opp to still be there so we can query the
	contact roles
for accounts, it's easier if we do things after delete	
*/

	system.debug ('TRIGGER FIRED');

	// first, make sure we have checked for contact role creation if necessary
	// (have to do this here to insure triggers happen in the right order)
	if ( Trigger.isInsert && ONEN_OppContactRoles.haveCheckedContactRoles == false ) {
		ONEN_OppContactRoles.CheckContactRoles ( trigger.newmap );	
	}

	// get membership rectype id's
	// TBD refactor this to class method?
	// TBD eventually this may be data-driven
	set<string> mbrRTNames = ONEN_Constants.OPP_RECTYPES_MEMBERSHIP;
	set<id> mbrRTids = new set<id> ();
	ONEN_RecordTypes rtlookup = new ONEN_RecordTypes ('Opportunity');
	for ( string thisRTName : mbrRTNames ) {
		id rtid = rtlookup.GetRecordTypeId (thisRTName);
		mbrRTids.add (rtid);
	}
	
	// new method for individual account lookup
	Id IndAcctId = ONEN_DefaultAccount.GetIndividualAccountId();
	system.debug ('ind acct: ' + IndAcctId);
	
	// for Individual opps we need to get list of the opp id (so we can later query the HH id and apply mbr status to all in HH)
	// OTOH, for Account opps we can just collect list of the accounts and apply status directly to those
	list<id> OppsToProcess = new list<id> ();
	set<id> AcctsToProcess = new set<id> ();
	
	boolean skipOpps = false;
	
//	system.debug (Trigger.New);
	
	// TBD need to be collecting 2 different sets of opps for processing: one for contact opps, one for account opps
	for ( Opportunity thisOpp : Trigger.IsDelete ? Trigger.old : Trigger.new ) {
		
		if ( (trigger.isInsert || trigger.isUndelete) && thisOpp.IsWon == true && mbrRTids.contains(thisOpp.RecordTypeId) ) {
			// if new opp/undelete, recalc if it's a membership & closedwon
			// add to the appropriate list based on whether account or contact based
			if ( thisOpp.AccountId != IndAcctId && thisOpp.AccountId != null ) {
				AcctsToProcess.add (thisOpp.AccountId);	
			} else {
				OppsToProcess.add (thisOpp.id);	
			}
			
		} else if ( trigger.isUpdate ) {
			// if an update, recalc if it's a membership and amt, stage, or any member field has chgd
			// or if it chgd from or chgd to a membership rec type
			
			Opportunity oldOpp = trigger.oldmap.get (thisOpp.id);
	//		system.debug ('new: ' + thisOpp.id + '    old: ' + oldOpp.id );
			if  ( 
			(	mbrRTids.contains(thisOpp.RecordTypeId) &&
				( thisOpp.IsWon != oldOpp.IsWon || 	
				thisOpp.Amount != oldOpp.Amount || 	
				thisOpp.Membership_End_Date__c != oldOpp.Membership_End_Date__c || 	
				thisOpp.Membership_Origin__c != oldOpp.Membership_Origin__c || 	
				thisOpp.Membership_Start_Date__c != oldOpp.Membership_Start_Date__c || 	
				thisOpp.Member_Level__c != oldOpp.Member_Level__c || 	
				thisOpp.Amount != oldOpp.Amount ||
				thisOpp.AccountId != oldOpp.AccountId )
			 ) || 
			 ( thisOpp.RecordTypeId != oldOpp.RecordTypeId && ( mbrRTids.contains(thisOpp.RecordTypeId) || mbrRTids.contains(oldOpp.RecordTypeId) ))
			 )
			{
				if ( thisOpp.AccountId != IndAcctId && thisOpp.AccountId != null ) {
					AcctsToProcess.add (thisOpp.AccountId);	
				} else {
					OppsToProcess.add (thisOpp.id);	
				}
			
			}
			
		} else if ( trigger.isDelete && mbrRTids.contains(thisOpp.RecordTypeId) && thisOpp.IsWon == true ) {
			if ( thisOpp.AccountId != IndAcctId && thisOpp.AccountId != null ) {
				if ( trigger.isAfter ) {
					// for accounts, process on AfterDelete
					AcctsToProcess.add (thisOpp.AccountId);	
				}
			} else {
				if ( trigger.isBefore ) {
					// for contacts, process on BeforeDelete (see above)
					OppsToProcess.add (thisOpp.id);	
				}
			}
			skipOpps = true;
		}
	}
	
	system.debug ('opps to process (for contacts): ' + OppsToProcess.size() );
	system.debug ('accounts to process: ' + AcctsToProcess.size() );
	
	if ( OppsToProcess.size () > 0 ) {

		list<Contact> ConsToUpdate = ONEN_MembershipRollup.RollupMemberStatusToContacts ( OppsToProcess, skipOpps, mbrRTids);
		update ConsToUpdate;
		
	}
	
	if ( AcctsToProcess.size() > 0 ) {
	
		list<Account> AcctsToUpdate = ONEN_MembershipRollup.RollupMemberStatusToAccounts (AcctsToProcess, mbrRTids);
		update AcctsToUpdate;
		
	}
	

}